package com.allin.mall.common.core.json;

import com.google.gson.Gson;
import org.reactivestreams.Publisher;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageDecoder;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MimeType;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public class GsonDecoder implements HttpMessageDecoder<Object> {

    private final Gson gson;

    public GsonDecoder(Gson gson) {
        this.gson = gson;
    }

    @Override
    public boolean canDecode(ResolvableType elementType, MimeType mimeType) {
        // 能被解码的格式， 如果需要text_plain也能被gson解析，需要添加判断
        return mimeType == null || mimeType.includes(MediaType.APPLICATION_JSON) || mimeType.includes(MediaType.TEXT_PLAIN);
    }

    @Override
    public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
        return Flux
                .from(inputStream)
                .collectList()
                .map(dataBuffers -> {
                    byte[] bytes = new byte[dataBuffers.stream().mapToInt(DataBuffer::readableByteCount).sum()];
                    int offset = 0;
                    for (DataBuffer buffer : dataBuffers) {
                        int length = buffer.readableByteCount();
                        buffer.read(bytes, offset, length);
                        offset += length;
                        DataBufferUtils.release(buffer);
                    }
                    return gson.fromJson(new String(bytes, StandardCharsets.UTF_8), elementType.getType());
                })
                .flux();
    }

    @Override
    public Mono<Object> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
        return decode(inputStream, elementType, mimeType, hints).singleOrEmpty();
    }

    @Override
    public List<MimeType> getDecodableMimeTypes() {
        return List.of(MediaType.APPLICATION_JSON);
    }


    @Override
    public Map<String, Object> getDecodeHints(ResolvableType actualType, ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) {
        return Map.of();
    }
}
